package com.zhiwen.config;


import com.sun.istack.internal.logging.Logger;
import com.zhiwen.dto.BeanDefine;
import com.zhiwen.service.UserServiceImpl;
import com.zhiwen.setannotation.DemoAnnotation;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class ClassPathXMLApplicationContext {
    Logger logger = Logger.getLogger(ClassPathXMLApplicationContext.class);
    //将Bean数据添加到集合中
    private List<BeanDefine> beanDefinesList = new ArrayList<BeanDefine>();
    private Map<String, Object> sigletions = new HashMap<String, Object>();

    public ClassPathXMLApplicationContext(String filename) {
        //读取xml文件管理bean
        this.readXml(filename);
        //实例化bean
        this.instancesBean();
        //注解处理器
        this.annotationInject();

    }

    /**
     * 注解处理器
     * 如果注解DemoAnnotation配置了name属性，则根据name所指定的名称获取要注入的实例引用，
     * 如果注解DemoAnnotation;没有配置name属性，则根据属性所属类型来扫描配置文件获取要
     * 注入的实例引用
     */
    private void annotationInject() {
        for (String beanName : sigletions.keySet()) {
            Object bean = sigletions.get(beanName);
            if (bean != null) {
                this.propertyAnnotation(bean);
                this.fieldAnnotation(bean);
            }
        }
    }

    /**
     * 处理在字段上的注解
     *
     * @param bean 处理的bean
     */
    private void fieldAnnotation(Object bean) {
        try {
            //获取其全部的字段描述
            Field[] fields = bean.getClass().getFields();
            //遍历字段名，判断是否有注解
            for (Field field : fields) {
                if (field != null && field.isAnnotationPresent(DemoAnnotation.class)) {
                    DemoAnnotation annotation = field.getAnnotation(DemoAnnotation.class);
                    String desc = "";
                    Object value = null;
                    if (annotation != null && "".equals(annotation.desc())) {
                        desc = annotation.desc();
                        value = sigletions.get(desc);
                    } else {
                        for (String key : sigletions.keySet()) {
                            //判断当前属性所属的类型是否在配置文件中存在
                            if (field.getType().isAssignableFrom(sigletions.get(key).getClass())) {
                                //获取类型匹配的实例对象
                                value = sigletions.get(key);
                                break;
                            }
                        }
                    }
                    //允许访问private方法
                    field.setAccessible(true);
                    //把引用对象注入属性
                    field.set(bean, value);
                }
            }
        } catch (IllegalAccessException e) {
            logger.info("字段注解解析异常..........");
        }
    }

    /**
     * 处理在set方法加入的注解
     *
     * @param bean 处理的bean
     */
    private void propertyAnnotation(Object bean) {
        try {
            //获取其属性的描述
            PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
            for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                //获取所有set方法
                Method setMethod = propertyDescriptor.getWriteMethod();
                //判断set方法是否定义了注解
                if (setMethod != null && setMethod.isAnnotationPresent(DemoAnnotation.class)) {
                    //获取当前注解，并判断name属性是否为空
                    DemoAnnotation annotation = setMethod.getAnnotation(DemoAnnotation.class);
                    String desc = annotation.desc();
                    Object value = null;
                    if (desc != null && !"".equals(annotation.desc())) {
                        //获取注解的name属性的内容
                        value = sigletions.get(annotation.desc());
                    } else {
                        for (String key : sigletions.keySet()) {
                            //如果当前注解没有指定name属性,则根据类型进行匹配
                            if (propertyDescriptor.getPropertyType().isAssignableFrom(sigletions.get(key).getClass())) {
                                value = sigletions.get(key);
                                break;
                            }
                        }
                    }
                    //允许访问private方法
                    setMethod.setAccessible(true);
                    //把引用对象注入属性
                    setMethod.invoke(bean, value);
                }
            }
        } catch (IntrospectionException e) {
            logger.info("set方法注解解析异常..........");
        } catch (IllegalAccessException e) {
            logger.info("set方法注解解析异常..........");
        } catch (InvocationTargetException e) {
            logger.info("set方法注解解析异常..........");
        }
    }

    /**
     * 实例化bean
     */
    private void instancesBean() {
        for (BeanDefine beanDefine : beanDefinesList) {
            try {
                sigletions.put(beanDefine.getId(), Class.forName(beanDefine.getClassUrl()).newInstance());
            } catch (InstantiationException e) {
                logger.info("实例化失败");
            } catch (IllegalAccessException e) {
                logger.info("实例化失败");
            } catch (ClassNotFoundException e) {
                logger.info("实例化失败");
            }
        }
    }

    /**
     * 读取Bean配置文件
     *
     * @param filename
     */
    private void readXml(String filename) {
        try {
            Document document = null;
            SAXReader saxReader = new SAXReader();
            //通过当前线程获取类加载器
            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
            //读取xml
            document = saxReader.read(contextClassLoader.getResourceAsStream(filename));
            Element beans = document.getRootElement();
            //将配置的类放入集合
            for (Iterator<Element> beanList = beans.elementIterator(); beanList.hasNext(); ) {
                Element element = beanList.next();
                BeanDefine beanDefine = new BeanDefine(element.attributeValue("id"), element.attributeValue("class"));
                beanDefinesList.add(beanDefine);
            }
        } catch (DocumentException e) {
            logger.info("读取配置文件失败");
        } catch (Exception e) {
            logger.info("读取配置文件失败");
        }
    }

    /**
     * 获取Map中的对应的bean实例
     *
     * @param beanId
     * @return
     */
    public Object getBean(String beanId) {
        return sigletions.get(beanId);
    }

    public static void main(String[] args) {
        ClassPathXMLApplicationContext path = new ClassPathXMLApplicationContext(
                "demo.xml");
        UserServiceImpl userService = (UserServiceImpl) path.getBean("userService");
        userService.show();
    }

}
